package com.zsy.consistency.hash.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;

@Slf4j
@Service
public class LoadBalanceImpl implements LoadBalance {

    private ConcurrentSkipListMap<Integer, ConcurrentHashMap<Object, Object>> serverMap = new ConcurrentSkipListMap<>();

    private static final long FNV_32_INIT = 2166136261L;
    private static final int FNV_32_PRIME = 16777619;


    @Override
    public Object put(Object key, Object o) {
        ConcurrentHashMap<Object, Object> server = obtainServer(key);
        server.put(key, o);
        return o;
    }

    @Override
    public Object get(Object key) {
        ConcurrentHashMap<Object, Object> server = obtainServer(key);
        return server.get(key);
    }

    @Override
    public void addServer(Object ip) {
        int hashIp = hash(ip);
        if (serverMap.containsKey(hashIp)) {
            log.info("已有该服务器");
            return;
        }
        serverMap.put(hashIp, new ConcurrentHashMap<>());
        Entry<Integer, ConcurrentHashMap<Object, Object>> nextServerEntry = obtainServerByHashKey(hashIp + 1);
        Integer nextServerKey = nextServerEntry.getKey();
        ConcurrentHashMap<Object, Object> nextServer = nextServerEntry.getValue();
        serverMap.put(nextServerKey, new ConcurrentHashMap<>());
        nextServer.forEach(this::put);
        log.info("{}服务器上线", ip);
    }

    @Override
    public void delServer(Object ip) {
        ConcurrentHashMap<Object, Object> server = obtainServer(ip);
        serverMap.remove(hash(ip));
        server.forEach(this::put);
        log.info("{}服务器下线", ip);
    }

    @Override
    public void destory() {
        serverMap.clear();
        log.info("所有服务器下线");
    }

    @Override
    public void start(String[] serverIps) {
        Arrays.stream(serverIps).forEach(this::addServer);
    }

    private ConcurrentHashMap<Object, Object> obtainServer(Object key) {
        int hashKey = hash(key);
        return obtainServerByHashKey(hashKey).getValue();
    }

    private Entry<Integer, ConcurrentHashMap<Object, Object>> obtainServerByHashKey(Integer hashKey) {
        Entry<Integer, ConcurrentHashMap<Object, Object>> entry = serverMap.ceilingEntry(hashKey);
        entry = entry == null ? serverMap.firstEntry() : entry;
        if (entry == null) {
            throw new RuntimeException("没有服务器");
        }
        return entry;
    }

    @Override
    public Integer hash(Object origin) {
        String s = origin.toString();
        int hash = (int) FNV_32_INIT;
        for (int i = 0; i < s.length(); i++) {
            hash = (hash ^ s.charAt(i)) * FNV_32_PRIME;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;
        hash = Math.abs(hash);
        return hash;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder(String.format("共%d个服务器：\n", serverMap.size()));
        serverMap.forEach((key, value) -> result.append("key:").append(key).append(" size:").append(value.size()).append("\n"));
        return result.toString();
    }
}
